#pragma once
#include <iostream>
#include "../mqcommon/Helper.hpp"
#include "../mqcommon/logger.hpp"
#include "../mqcommon/mq_msg.pb.h"
#include <mutex>
#include <memory>
#include <google/protobuf/map.h>
namespace mq_zxws
{
    // 交换机类
    struct Exchange{
        using ptr = std::shared_ptr<Exchange>;
        std::string name; // 交换机名称
        ExchangeType type; // 交换机类型
        bool durable; // 是否是持久化
        bool auto_delete; // 是否自动删除
        google::protobuf::Map<std::string, std::string> args; // 其他参数
        Exchange(){}
        Exchange(const std::string& ename,
                ExchangeType etype,
                bool edurable,
                bool eauto_delete,
                const google::protobuf::Map<std::string, std::string>& eargs)
            :name(ename), type(etype), durable(edurable), auto_delete(eauto_delete), args(eargs)
        {}
        // 将数据 key=val&key=val... 放入args
        void setArgs(const std::string & str_args){
            std::vector<std::string> sub_str;
            StrHelper::splite(str_args, "&", sub_str);
            for(const auto& str:sub_str){
                size_t pos = str.find('=');
                std::string key = str.substr(0, pos);
                std::string val = str.substr(pos + 1);
                args[key] = val;
            }
        }
        // 将 args 中的数据变成字符串 key=val&key=val...
        std::string getArgs(){
            std::string result;
            for(auto it = args.begin(); it != args.end(); ++it){
                result += it->first + "=" + it->second + "&";
            }
            return result;
        }
    };

    // 管理数据持久化
    class ExchangeMapper{
    public:
        ExchangeMapper(const std::string& dbfile):_sql_helper(dbfile){
            std::string parent = FileHelper::parentDirectory(dbfile);
            FileHelper::createDirectory(parent);
            assert(_sql_helper.open());
            createTable();
        }
        void createTable(){
            std::string sql = "create table if not exists exchange_table(\
                name varcahr(32) primary key, \
                type int, \
                durable int, \
                auto_delete int, \
                args varchar(128));";
            if(!_sql_helper.exec(sql, nullptr, nullptr)){
                DLOG("创建交换机表失败");
                abort();
            }
        }
        void removeTable(){
            std::string sql = "drop table if exists exchange_table;";
            if(!_sql_helper.exec(sql, nullptr, nullptr)){
                DLOG("删除交换机表失败");
                abort();
            }
        }
        bool insert(Exchange::ptr& exchange){
            char sql[4096]{};
            sprintf(sql, "insert into exchange_table values('%s', %d, %d, %d, '%s');", 
                    (exchange->name).c_str(), exchange->type, exchange->durable, exchange->auto_delete, exchange->getArgs().c_str());
            return _sql_helper.exec(sql, nullptr, nullptr);
        }
        void remove(const std::string& ename){
            std::string sql = "delete from exchange_table where name = '" + ename + "';";
            if(!_sql_helper.exec(sql, nullptr, nullptr)){
                DLOG("创建交换机表失败");
                abort();
            }
        }
        
        std::unordered_map<std::string, Exchange::ptr> recovery(){
            std::unordered_map<std::string, Exchange::ptr> result;
            std::string sql = "select * from exchange_table;";
            _sql_helper.exec(sql, SelectCallback, &result);
            return result;
        }

    private:
        static int SelectCallback(void *args, int col, char ** row, char ** col_name){
            using ExchangeMap = std::unordered_map<std::string, Exchange::ptr>;
            ExchangeMap* res = static_cast<ExchangeMap*>(args);
            auto exp = std::make_shared<Exchange>();
            exp->name = row[0];
            exp->type = (ExchangeType)std::stoi(row[1]);
            exp->durable = (bool)std::stoi(row[2]);
            exp->auto_delete = (bool)std::stoi(row[3]);
            if(row[4]) exp->setArgs(row[4]);
            res->insert(std::make_pair(exp->name, exp));
            return 0;
        }
        SqliteHelper _sql_helper;
    };

    class ExchangeManager{
    public:
        using ptr = std::shared_ptr<ExchangeManager>;
        ExchangeManager(const std::string& dbfile):_mapper(dbfile){
            _exchanges = _mapper.recovery();
        }
        // 声明交换机
        bool declareExchange(const std::string& name,
                ExchangeType type,
                bool durable,
                bool auto_delete,
                const google::protobuf::Map<std::string, std::string>& args)
        {
            std::lock_guard<std::mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            if(it == _exchanges.end()){
                auto exp = std::make_shared<Exchange>(name, type, durable, auto_delete, args);
                if(durable) {// 需要持久化的数据才需要持久化操作
                    if(!_mapper.insert(exp))
                        return false;
                }
                _exchanges.insert(std::make_pair(name, exp));
            }
            return true;
        }
        // 删除交换机
        void deleteExchange(const std::string & name){
            std::lock_guard<std::mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            if(it == _exchanges.end()) return;
            _exchanges.erase(it);
            if(it->second->durable) _mapper.remove(name); // 被持久化的数据才能被删除
        }
        // 获取指定交换机接口
        Exchange::ptr selectOne(const std::string & name){
            std::lock_guard<std::mutex> lock(_mutex);
            auto it = _exchanges.find(name);
            if(it == _exchanges.end()) return nullptr;
            return it->second;
        }
        // 交换机是否存在
        bool exists(const std::string& name){
            std::lock_guard<std::mutex> lock(_mutex);
            return _exchanges.find(name) != _exchanges.end();
        }
        // 清空交换机
        void clear(){
            std::lock_guard<std::mutex> lock(_mutex);
            _mapper.removeTable();
            _exchanges.clear();
        }
        size_t size(){
            std::lock_guard<std::mutex> lock(_mutex);
            return _exchanges.size();
        }
    private:
        std::mutex _mutex;
        ExchangeMapper _mapper;
        std::unordered_map<std::string, Exchange::ptr> _exchanges;
    };
}